package com.itunic.cloud.configuration;

import com.itunic.cloud.domain.CustomOAuth2User;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.util.AntPathMatcher;
import reactor.core.publisher.Mono;

/**
 * 鉴权核心逻辑
 * 通过接口ReactiveAuthorizationManager实现。
 *
 * @param <T>
 * @author Sean
 */
public class ReactiveBasicAuthorizeConfigManager<T> implements ReactiveAuthorizationManager<T> {

    /**
     * url匹配器
     */
    AntPathMatcher matcher = new AntPathMatcher();

    /**
     * 实现校验逻辑
     * 以下代码，只讲述了验证逻辑思路。
     * 并不能直接应用到实际生产中。
     *
     * @param authentication
     * @param object
     * @return
     */
    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, T object) {
        //判断当前是否为AuthorizationContext，如果不是，一律放行。
        //这里可以根据自己的业务逻辑定制了。
        //todo 在这里可以先判断当前URL是否需要权限校验。如果不需要，则代表不需要token，直接放行。
        if (object instanceof AuthorizationContext) {
            AuthorizationContext context = (AuthorizationContext) object;
            //开始验证需要鉴权的url
            return authentication
                    .filter(a ->
                            //过滤已经token验证通过的。
                            //如果此处为false，代表token无效，直接返回401状态码
                            a.isAuthenticated()
                    ).map(f -> {
                        //将当前鉴权的用户信息构造到http header中，方便下游服务需要获取用户信息。
                        CustomOAuth2User customOAuth2User = (CustomOAuth2User) f.getPrincipal();
                        context.getExchange().getRequest()
                                .mutate()
                                .header("uid", customOAuth2User.getId());
                        return f;
                    })
                    .flatMapIterable(a -> a.getAuthorities())
                    //获取用户权限集合
                    .map(g -> g.getAuthority())
                    .any(a -> {
                        //权限校验的核心逻辑，这里是伪代码，但思路是正确滴！
                        //1,判断当前请求的URL 需要什么权限。
                        boolean match = matcher.match("/aaa/**",
                                context.getExchange().getRequest().getPath().value());
                        //2，拿到权限集合后，判断当前用户是否持有该权限。
                        //有权限则返回true，没有则返回false
                        if (match) {
                            return a.equals("admin");
                        }
                        return false;
                    })
                    //构造权限决策信息
                    .map(hasAuthority -> new AuthorizationDecision(hasAuthority))
                    //如果结果为空，则直接拒绝。
                    .defaultIfEmpty(new AuthorizationDecision(false));
        } else {
            return Mono.just(
                    true
            ).map(f -> new AuthorizationDecision(f))
                    .defaultIfEmpty(new AuthorizationDecision(false));
        }

    }
}